﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Disposables;
using System.Linq;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using SoftSource.Common;
using SoftSource.MVVMC;
using SoftSource.Reactive;
using SoftSource.Timecard.Converters;
using SoftSource.Timecard.Resources;
using SoftSource.Timecard.ViewModelInterfaces;
using SoftSource.Timecard.Web.Data;

namespace SoftSource.Timecard.Views
{
    // HACK
    /// <summary>
    /// This converter is a silly hack to alow us to bind to a subset of a larger list.
    /// The use was that we have a list of WeekEntrySummaries that we need to display.
    /// The first week summary object needs to be displayed in the first column, the second in the second column, etc.
    /// Here we bind to main list, get the iterator, and just iterate over the list in order.
    /// Now if we call the converter on the first column, we get the first summary, and the same for the second, third, and so on.
    /// </summary>
    public class UtilizationSummaryWeekConverter : IValueConverter
    {
        public UtilizationSummaryWeekConverter()
        {
            this.Clear();
        }

        Dictionary<int, IEnumerator<UtilizationSummaryView.UserWeekEntrySummaries>> iterators;

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var summary = value as UtilizationSummaryView.UserEntrySummaries;

            if (summary != null)
            {
                if (!this.iterators.ContainsKey(summary.User.UserID))
                    this.iterators[summary.User.UserID] = summary.EntrySummaries.GetEnumerator();

                var iterator = this.iterators[summary.User.UserID];

                if (iterator.MoveNext())
                {
                    return iterator.Current.EntrySummaries;
                }
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion

        internal void Clear()
        {
            this.iterators = new Dictionary<int, IEnumerator<UtilizationSummaryView.UserWeekEntrySummaries>>();
        }
    }

    public partial class UtilizationSummaryView : UserControl, IDisposable, IView<IUtilizationSummaryViewModelConsume>
    {
        #region Fields
        private CompositeDisposable compositeDisposable;
        #endregion Fields

        #region Custom Classes

        public class UserEntrySummaries : INotifyPropertyChanged
        {
            private UserSummary user;
            public UserSummary User
            {
                get { return this.user; }
                set
                {
                    this.user = value;
                    this.PropertyChanged.Notify(() => this.User);
                }
            }

            private IEnumerable<UserWeekEntrySummaries> entrySummaries;
            public IEnumerable<UserWeekEntrySummaries> EntrySummaries
            {
                get { return this.entrySummaries; }
                set
                {
                    this.entrySummaries = value;
                    this.PropertyChanged.Notify(() => this.EntrySummaries);
                }
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }

        public class UserWeekEntrySummaries : INotifyPropertyChanged
        {
            private DateTime startOfWeek;
            public DateTime StartOfWeek
            {
                get { return this.startOfWeek; }
                set
                {
                    this.startOfWeek = value;
                    this.PropertyChanged.Notify(() => this.StartOfWeek);
                }
            }

            private IEnumerable<EntrySummary> entrySummaries;
            public IEnumerable<EntrySummary> EntrySummaries
            {
                get { return this.entrySummaries; }
                set
                {
                    this.entrySummaries = value;
                    this.PropertyChanged.Notify(() => this.EntrySummaries);
                }
            }

            #region INotifyPropertyChanged Members

            public event PropertyChangedEventHandler PropertyChanged;

            #endregion
        }

        #endregion Custom Classes

        public UtilizationSummaryView()
        {
            InitializeComponent();

            this.compositeDisposable = new CompositeDisposable();

            this.Loaded += new RoutedEventHandler(UtilizationSummaryView_Loaded);
        }

        #region Events

        void UtilizationSummaryView_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.ViewModel != null)
            {
                this.Busy.BusyContent = ApplicationStrings.LoadingMessage;
                this.Busy.IsBusy = true;

                this.ViewModel.Load // Load the data required to use this ViewModel
                    .Nullable()
                    .Subscribe(unit => this.Busy.IsBusy = false);
            }
        }

        private void Refresh_Click(object sender, RoutedEventArgs e)
        {
            this.Busy.BusyContent = ApplicationStrings.LoadingMessage;
            this.Busy.IsBusy = true;

            this.ViewModel
                .LoadEntrySummaries(this.UtilizationDateRangeSelector.StartDate, this.UtilizationDateRangeSelector.EndDate)
                .Subscribe(unit =>
                    {
                        this.Busy.IsBusy = false;
                        this.Initialize();
                    });
        }

        #endregion Events

        #region Methods

        private void Initialize()
        {
            UserIDConverter userConverter = new UserIDConverter();
            var entries = from userEntries in this.ViewModel.Entries.Value.GroupBy(entry => entry.UserID) // Group entries by user
                          let userID = userEntries.Key // Get the userId that these entries are currently associated with
                          select new UserEntrySummaries
                          {
                              User = userConverter.Convert<UserSummary>(userID),
                              EntrySummaries = from userWeekEntries in userEntries.GroupBy(entry => entry.Date.StartOfWeek(DayOfWeek.Monday)).OrderBy(g => g.Key) // Group into weeks
                                               let startOfWeek = userWeekEntries.Key // Get the start of the week
                                               select new UserWeekEntrySummaries
                                               {
                                                   StartOfWeek = startOfWeek,
                                                   EntrySummaries = userWeekEntries.OrderBy(entry => entry.Date)
                                               }
                          };

            List<DateTime> weeks = entries
                .Select(ues => ues.EntrySummaries
                    .Select(uwes => uwes.StartOfWeek))
                .Aggregate(Enumerable.Union)
                .OrderBy(d => d)
                .ToList(); // Get a list of the week start dates

            this.UserDataGrid.Columns.Clear();

            var converter = this.Resources["UtilizationSummaryWeekConverter"] as UtilizationSummaryWeekConverter;
            converter.Clear();

            DataGridTextColumn nameColumn = new DataGridTextColumn();
            nameColumn.Header = "Name";
            nameColumn.CanUserSort = false;
            nameColumn.Binding = new Binding("User.FullName");
            this.UserDataGrid.Columns.Add(nameColumn);

            foreach (var week in weeks) // Manually insert a column for each week
            {
                DataGridTemplateColumn column = new DataGridTemplateColumn();
                column.CellTemplate = this.Resources["EntryGroupDataTemplate"] as DataTemplate;
                column.Header = string.Format("Week of {0}", week.ToShortDateString());
                column.CanUserSort = false;

                this.UserDataGrid.Columns.Add(column);
            }

            this.UserDataGrid.ItemsSource = null;
            this.UserDataGrid.ItemsSource = entries.OrderBy(ues => ues.User.FullName);
        }

        #endregion Methods

        #region IView<IUtilizationSummaryViewModelConsume> Members

        public IUtilizationSummaryViewModelConsume ViewModel
        {
            get { return this.DataContext as IUtilizationSummaryViewModelConsume; }
            set { this.DataContext = value; }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this.compositeDisposable.Dispose();
            GC.SuppressFinalize(this);
        }

        #endregion

        ~UtilizationSummaryView()
        {
            this.Dispose();
        }
    }
}
